package orbitPrototype.render;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;

import orbitPrototype.Settings;
import orbitPrototype.Util;
import orbitPrototype.game.Game;
import orbitPrototype.loaders.SpriteSheetLoader;
import orbitPrototype.objects.DisplayObject;
import orbitPrototype.objects.DisplayObjectContainer;
import orbitPrototype.objects.SimpleShape;
import orbitPrototype.objects.Sprite;

public class Render implements Runnable
{
	private DisplayObjectContainer _mainDisplayObjectContainer = null;
	private BufferedImage _bufferedImage = null;
	private BufferStrategy _bufferStrategy = null;
	
	private Color _bgColor = Color.white;
	
	private Graphics _graphics = null;
	private Graphics2D _graphics2d = null;
	
	private long _fpsStartTime;
	private long _fpsCurrentTime;
	private long _fpsMax = 60;
		
	public Render(Game game, BufferedImage bufferedImage, BufferStrategy bufferStrategy)
	{
		_mainDisplayObjectContainer = game.getMainDisplayObjectContainer();
		_bufferedImage = bufferedImage;
		_bufferStrategy = bufferStrategy;
	}

	@Override
	public void run()
	{
		
		while(true)
		{
			_fpsStartTime = Util.getCurrentTick();
			
			_graphics2d = _bufferedImage.createGraphics();
			_graphics2d.setColor(_bgColor);
			_graphics2d.fillRect(0, 0, Settings.FRAME_WIDTH, Settings.FRAME_HEIGHT);
			
			/*
			 * START RENDER LOGIC
			 */
			
			drawDisplayObjectContainer(_mainDisplayObjectContainer, new Point(_mainDisplayObjectContainer.getX(), _mainDisplayObjectContainer.getY()));
			
			//_graphics2d.setColor(Color.BLUE);
			//_graphics2d.setFont(new Font("Arial", Font.BOLD, 16));
			//_graphics2d.drawString("QWERTY qwerty", 0, 18);
			
			/*
			 * END RENDER LOGIC
			 */
			
			_graphics = _bufferStrategy.getDrawGraphics();
			_graphics.drawImage(_bufferedImage,0,0,null);
			
			if(!this._bufferStrategy.contentsLost())
			{
				this._bufferStrategy.show();
			}

			// Dispose graphic resources
			if(this._graphics != null)
			{
				this._graphics.dispose();
			}
			
			if(this._graphics2d != null)
			{
				this._graphics2d.dispose();
			}
			
	        _fpsCurrentTime = Util.getCurrentTick();
	        long fpsFrameTime = _fpsCurrentTime - _fpsStartTime;
	        long sleep = 1000 / _fpsMax - fpsFrameTime;
	        if(sleep > 0)
	        {
	        	try 
	        	{
					Thread.sleep(sleep);
				} 
	        	catch (InterruptedException e) 
	        	{
					e.printStackTrace();
				}					
			}
		}
	}
	
	/**
	 * Description: Goes through displayObjectContainer and draws all subobjects. Takes container offset into consideration.
	 * @param displayObjectContainer
	 * @param offsetPoint
	 */
	private void drawDisplayObjectContainer(DisplayObjectContainer displayObjectContainer, Point offsetPoint)
	{
		for(int i = displayObjectContainer.getNumChildren() - 1; i >= 0; i--)
		{
			if(displayObjectContainer.getChildAt(i).getGraphicsType() == DisplayObject.TYPE_CONTAINER && displayObjectContainer.getChildAt(i).getVisible())
			{
				DisplayObjectContainer childDisplayObjectContainer = (DisplayObjectContainer) displayObjectContainer.getChildAt(i);
				drawDisplayObjectContainer(childDisplayObjectContainer, new Point(offsetPoint.x + childDisplayObjectContainer.getX(), offsetPoint.y + childDisplayObjectContainer.getY()));
			}
			else if(displayObjectContainer.getChildAt(i).getGraphicsType() == DisplayObject.TYPE_SHAPE && displayObjectContainer.getChildAt(i).getVisible())
			{
				drawShape((SimpleShape) displayObjectContainer.getChildAt(i), offsetPoint);
			}
			else if(displayObjectContainer.getChildAt(i).getGraphicsType() == DisplayObject.TYPE_SPRITE && displayObjectContainer.getChildAt(i).getVisible())
			{
				drawSprite((Sprite) displayObjectContainer.getChildAt(i), offsetPoint);
			}
		}
		
		if(Settings.RENDER_DRAW_CONTAINER_BOUNDING_BOX 
				&& displayObjectContainer.getNumChildren() > 0
				&& displayObjectContainer.getVisible())
		{
			Rectangle boundingBox = displayObjectContainer.getBoundingBox();
			_graphics2d.setColor(Color.red);
			_graphics2d.drawRect(boundingBox.x + offsetPoint.x, boundingBox.y + offsetPoint.y, boundingBox.width, boundingBox.height);
			
//			_graphics2d.setColor(Color.green);
//			_graphics2d.drawRect(displayObjectContainer.getX() + offsetPoint.x, displayObjectContainer.getY() + offsetPoint.y, displayObjectContainer.getWidth(), displayObjectContainer.getHeight());
		}
	}
	
	/**
	 * Description: Draws SimpleShape, with proper offset.
	 * @param simpleShape
	 * @param offsetPoint
	 */
	private void drawShape(SimpleShape simpleShape, Point offsetPoint)
	{
		_graphics2d.setColor(simpleShape.getColor());
		_graphics2d.fillRect(simpleShape.getX() + offsetPoint.x, simpleShape.getY() + offsetPoint.y, simpleShape.getWidth(), simpleShape.getHeight());
	}
	
	/**
	 * Description: Draws Sprite, with proper offset.
	 * @param sprite
	 * @param offsetPoint
	 */
	private void drawSprite(Sprite sprite, Point offsetPoint)
	{
		//Rotates around center point only, for now
		_graphics2d.rotate(sprite.getRotation(), (sprite.getX() + (sprite.getWidth() / 2)) + offsetPoint.x, (sprite.getY() + (sprite.getHeight() / 2)) + offsetPoint.y);		
		
		BufferedImage sourceImage = SpriteSheetLoader.getSheet(sprite.getSpriteType());
		
		int dx1 = sprite.getX() + offsetPoint.x;
		int dy1 = sprite.getY() + offsetPoint.y;
		int dx2 = dx1 + sprite.getWidth();
		int dy2 = dy1 + sprite.getHeight();
		
		int sx1 = sprite.getSourceRectangle().x;
		int sy1 = sprite.getSourceRectangle().y;
		int sx2 = sx1 + sprite.getSourceRectangle().width;
		int sy2 = sy1 + sprite.getSourceRectangle().height;
		
		_graphics2d.drawImage(sourceImage, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
		
		//Resets rotation
		_graphics2d.rotate(-sprite.getRotation(), (sprite.getX() + (sprite.getWidth() / 2)) + offsetPoint.x, (sprite.getY() + (sprite.getHeight() / 2)) + offsetPoint.y);
	}
}
